package git.soulbgm.common.mapper;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

import java.util.List;
import java.util.Objects;

import static java.util.stream.Collectors.joining;

/**
 * 插入多条数据
 *
 * @author SoulBGM
 * @since 2022-05-14
 */
public class UpsertList extends AbstractMethod {

    public UpsertList() {
        super("upsertList");
    }

    /**
     * @param name 方法名
     */
    public UpsertList(String name) {
        super(name);
    }

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
        String sql = "<script>UPSERT INTO %s %s %s</script>";
        //upsert into %s (字段1,字段2,...) select 字段1值,字段2值,... union all select 字段1值,字段2值,...;
        String columnScript = SqlScriptUtils.convertTrim(getAllInsertSqlColumn(tableInfo),
                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);

        String oneTrim = SqlScriptUtils.convertTrim(getAllInsertSqlProperty("record.", tableInfo),
                "VALUES(", ")", "", ",");
        String oneForeach = SqlScriptUtils.convertForeach(oneTrim, "list", "", "record", "");

        String moreTrim = SqlScriptUtils.convertTrim(getAllInsertSqlProperty("record.", tableInfo),
                "select", "", "", ",");
        String moreForeach = SqlScriptUtils.convertForeach(moreTrim, "list", "", "record", "union all");
        String valuesScript = SqlScriptUtils.convertChoose("null != list and list.size == 1", oneForeach, moreForeach);

        String keyProperty = null;
        String keyColumn = null;
        // 表包含主键处理逻辑,如果不包含主键当普通字段处理
        if (tableInfo.havePK()) {
            // 自增主键会造成HBase单Region数据挤压，直接移除
            if (null != tableInfo.getKeySequence()) {
                keyGenerator = TableInfoHelper.genKeyGenerator(this.methodName, tableInfo, builderAssistant);
                keyProperty = tableInfo.getKeyProperty();
                keyColumn = tableInfo.getKeyColumn();
            }
        }
        String sqlResult = String.format(sql, tableInfo.getTableName(), columnScript, valuesScript);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, "upsertList", sqlSource, keyGenerator, keyProperty, keyColumn);
    }

    /**
     * 获取所有 upsert 时候插入值 sql 脚本片段
     * <p>upsert into table (字段) values (值)</p>
     * <p>位于 "值" 部位</p>
     *
     * <li> 自动选部位,根据规则会生成 不带if 标签 </li>
     *
     * @return sql 脚本片段
     */
    public String getAllInsertSqlProperty(final String prefix, final TableInfo tableInfo) {
        final List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        final String newPrefix = prefix == null ? EMPTY : prefix;
        return tableInfo.getKeyInsertSqlProperty(false, newPrefix, false) + fieldList.stream()
                .map(i -> i.getInsertSqlProperty(newPrefix)).filter(Objects::nonNull).collect(joining());
    }

    /**
     * 获取 upsert 时候字段 sql 脚本片段
     * <p>upsert into table (字段) values (值)</p>
     * <p>位于 "字段" 部位</p>
     *
     * <li> 自动选部位,根据规则会生成 不带if 标签 </li>
     *
     * @return sql 脚本片段
     */
    public String getAllInsertSqlColumn(final TableInfo tableInfo) {
        final List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        return tableInfo.getKeyInsertSqlColumn(false, null, false) + fieldList.stream()
                .map(TableFieldInfo::getInsertSqlColumn).filter(Objects::nonNull).collect(joining());
    }

}
